
// import * as d3 from "../lib/d3.js";
import * as d3 from "d3";
import {lift, squash} from "./util.js";
import {$} from "../util/seq.js";
import {invoke} from "../util/functions.js";
import {isHoverSupported} from "../util/context.js";

function bindControls(tooltips) {
    const tooltipIds = new Set(tooltips.nodes().map(e => e.id));
    return d3.selectAll("[aria-labelledby]").filter(function() {
        const control = d3.select(this);
        const ids = control.attr("aria-labelledby").split(/\s+/);
        for (let i = 0; i < ids.length; i++) {
            const id = ids[i];
            if (tooltipIds.has(id)) {
                // [side-effect] remember the tooltip id for this control
                this.dataset.tooltip = id;
                // [side-effect] set the title attribute to support basic tooltip on hover
                control.attr("title", d3.select(`#${id}`).text().trim());
                return true;
            }
        }
    });
}

export function defineTooltips(tooltips) {
    const controls = bindControls(tooltips);
    let hider;

    // CONSIDER: use a different delay depending on input. focus would be immediate. hover would be delayed.
    //           this would avoid tooltip flashing while the mouse pointer moves around, but would give immediate
    //           feedback when tabbing through controls.

    function hide(wait) {
        clearTimeout(hider);
        hider = invoke(() => tooltips.classed("show", false), wait);
    }

    function show(tooltip) {
        clearTimeout(hider);
        tooltips.classed("show", false);
        tooltip.classed("show", !!tooltip.text());  // Show the tooltip if not empty.
    }

    function handleShow() {
        show(d3.select(`#${this.dataset.tooltip}`));
    }

    // click the tooltip to dismiss it
    tooltips.on($`click`, event => {
        hide();
        squash(event);
    });

    controls
        .on($`focus`, handleShow)
        .on($`blur`, () => hide(1));

    if (isHoverSupported()) {
        controls
            .on($`mouseenter`, handleShow)
            .on($`mouseleave`, () => hide(250));
    } else /* primary interaction is touch */ {
        controls.filter(lift(e => !e.classed("no-touch-tt")))
            .on($`click`, handleShow)
            .on($`mouseleave`, () => hide(1));
    }

    return {
        hideTooltip() {
            return tooltips
                .filter(lift(e => e.classed("show")))
                .classed("show", false)
                .nodes().length > 0;
        },
        showTooltip: show,
    };
}
